#pragma once

#include <iostream>
#include <string>
#include <vector>
#include <unordered_map>
#include <sstream>

const std::string Sep = "\r\n";
const std::string LineSep = " ";
const std::string HeaderLineSep = ": ";
const std::string BlankLine = Sep;
const std::string HttpVersion = "HTTP/1.0";
const std::string DefaultHomePage = "wwwroot/";
const std::string Page404 = "wwwroot/404.html";
const std::string FirstPage = "index.html";

class HttpRequest
{
private:
    // 将请求行的每个部分细化
    void ParseReqLine(const std::string &_req_line, const std::string &sep)
    {
        std::stringstream ss(_req_line);
        ss >> _method >> _uri >> _version;
    }

    // 将请求报头的KV细化
    bool ParseHeaderkv()
    {
        std::string key, value;
        for (auto &header : _req_header)
        {
            if (SplitString(header, &key, &value, HeaderLineSep))
            {
                _header_kv.insert(std::make_pair(key, value));
            }
        }
        return true;
    }

    // 解析出报头尾插到_req_header中--复用ParseOneLine
    bool ParseHeader(std::string &req_str)
    {
        std::string line;
        while (true)
        {
            bool r = ParseOneLine(req_str, &line, Sep);
            if (r && !line.empty())
            {
                _req_header.push_back(line);
            }
            else if (r && line.empty()) // 读到了空行
            {
                _blank_line = BlankLine;
                break;
            }
            else
            {
                return false;
            }
        }
        ParseHeaderkv();
        return true;
    }

public:
    HttpRequest()
    {
    }
    ~HttpRequest()
    {
    }

    void Deserialize(std::string &req_str)
    {
        if (ParseOneLine(req_str, &_req_line, Sep))
        {
            ParseReqLine(_req_line, LineSep);
            ParseHeader(req_str);
            _body = req_str;

            if (_method == "POST")
            {
                _isexec = true;
                _path = _uri;
                _args = _body;
            }
            else if (_method == "GET")
            {
                auto pos = _uri.find("?");
                if (pos != std::string::npos)
                {
                    _isexec = true;
                    _path = _uri.substr(0, pos);
                    _args = _uri.substr(pos + 1);
                }
            }
        }
        Print();
    }

    void Print()
    {
        std::cout << "_method: " << _method << std::endl;
        std::cout << "_uri: " << _uri << std::endl;
        std::cout << "_version: " << _version << std::endl;

        for (auto &kv : _header_kv)
        {
            std::cout << kv.first << " # " << kv.second << std::endl;
        }
        std::cout << "_blank_line: " << _blank_line;
        std::cout << "_body: " << _body << std::endl;
    }

    std::string GetContent(const std::string &path)
    {
        std::string content;
        std::ifstream in(path, std::ios::binary);
        if (!in.is_open())
            return std::string();
        in.seekg(0, in.end);
        int filesize = in.tellg();
        in.seekg(0, in.beg);
        content.resize(filesize);
        in.read((char *)content.c_str(), filesize);
        in.close();
        return content;

        // 暂时写法
        // std::string content;
        // std::ifstream in(path);
        // if (!in.is_open())
        //     return std::string();
        // std::string line;
        // while (std::getline(in, line))
        //     content += line;
        // in.close();
        // return content;
    }

    std::string Uri()
    {
        return _uri;
    }

    void SetUri(const std::string &newuri)
    {
        _uri = newuri;
    }

    std::string Suffix()
    {
        auto pos = _uri.find(".");
        if (pos == std::string::npos)
            return std::string(".html");
        return _uri.substr(pos);
    }

    bool IsHasArgs()
    {
        return _isexec;
    }

    std::string Path() { return _path; }
    std::string Args() { return _args; }

private:
    std::string _req_line;                // 请求行
    std::vector<std::string> _req_header; // 请求报头
    std::string _blank_line;              // 空行
    std::string _body;                    // 请求正文
    std::string _path;                    // 请求路径
    std::string _args;                    // 请求参数
    bool _isexec;

    // 细化的内容
    std::string _method;                                     // 请求方式
    std::string _uri;                                        // 请求资源路径
    std::string _version;                                    // HTTP版本
    std::unordered_map<std::string, std::string> _header_kv; // 请求报头KV
};

class HttpResponse
{
private:
    std::string Code2Desc(int code)
    {
        switch (code)
        {
        case 200:
            return "OK";
        case 404:
            return "Not Found";
        default:
            return std::string();
        }
    }

    std::string Suffix2String(const std::string &suffix)
    {
        if (suffix == ".html")
            return "text/html";
        else if (suffix == ".jpg")
            return "application/x-jpg";
        else
            return "text/html";
    }

public:
    HttpResponse()
        : _version(HttpVersion), _blank_line(BlankLine)
    {
    }
    ~HttpResponse()
    {
    }

    void SetHeader(const std::string &k, const std::string &v)
    {
        _header_kv[k] = v;
    }

    void Build(HttpRequest &req)
    {
        std::string uri = DefaultHomePage + req.Uri();
        if (uri.back() == '/')
        {
            uri += FirstPage;
        }
        _content = req.GetContent(uri);
        if (_content.empty())
        {
            _status_code = 404;
            _content = req.GetContent(Page404);
        }
        else
        {
            _status_code = 200;
        }
        _status_desc = Code2Desc(_status_code);

        if (_content.size())
        {
            SetHeader("content-Length", std::to_string(_content.size()));
        }
        std::string mime_type = Suffix2String(req.Suffix());
        SetHeader("Content-Type", mime_type);
    }

    void Serialize(std::string *resp_str)
    {
        _resp_line = _version + LineSep + std::to_string(_status_code) + LineSep + _status_desc;
        for (auto &kv : _header_kv)
        {
            _resp_header.push_back(kv.first + HeaderLineSep + kv.second);
        }
        _body = _content;

        *resp_str = _resp_line + Sep;
        for (auto &line : _resp_header)
        {
            *resp_str += line + Sep;
        }
        *resp_str += _blank_line;
        *resp_str += _body;
    }

    void SetCode(int code)
    {
        _status_code = code;
        _status_desc = Code2Desc(code);
    }

    void SetContent(const std::string &content)
    {
        _content = content;
    }

    std::string GetMonthName(int month)
    {
        std::vector<std::string> months = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
        return months[month];
    }

    std::string GetWeekDayName(int day)
    {
        std::vector<std::string> weekdays = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
        return weekdays[day];
    }

    std::string ExpireTimeUseRfc1123(int t)
    {
        time_t timeout = time(nullptr) + t; 
        // 这里不能用localtime，因为localtime是默认带了时区的。gmtime获取的就是UTC统一时间
        struct tm* tm= gmtime(&timeout);
        char timebuffer[1024];
        snprintf(timebuffer, sizeof(timebuffer), "%s, %02d %s %04d %2d:%02d:%02d UTC",
        GetWeekDayName(tm->tm_wday).c_str(), tm->tm_mday, 
        GetMonthName(tm->tm_mon).c_str(), tm->tm_year + 1900, 
        tm->tm_hour, tm->tm_min, tm->tm_sec
        );
        return timebuffer;
    }

private:
    // 细化的内容
    std::string _version;                                    // HTTP版本
    int _status_code;                                        // 状态码
    std::string _status_desc;                                // 状态码描述
    std::string _content;                                    // 获取的内容
    std::unordered_map<std::string, std::string> _header_kv; // 响应报头KV

    std::string _resp_line;                // 状态行
    std::vector<std::string> _resp_header; // 响应报头
    std::string _blank_line;               // 空行
    std::string _body;                     // 响应正文
};